import React, {Component} from "react";
import {ArrayContainer} from "../array/ArrayContainer";
import Graph from "../Dynamic/Dynamic";
import $ from "jquery";
import Menu from "./menu";
import Navbar from "../Dynamic/navbar";
import {Card} from "./Card";
import {ArrayContainer2} from "../array/ArrayContainer2";
import {ColorContainer} from "../array/ColorContainer";
import InitialArray from "./table";
import TempArray from "./tableForTempArray";
import GRAPH from "../Greedy/help/GRAPH";
import TableForCompare from "./tableForCompare";
let before = false;
let pause = false;
let reset = false;
export default class Sort extends Component {
    constructor() {
            super();
            this.state = {
                time: 600,
                reset: false,
                algo: 0,
                originalArray: [1, 2, 3, 4, 5, 6, 7, 8],
                array:[1, 2, 3, 4, 5, 6, 7, 8],
                source:-1,
                pivot:-1,
                destination:-1,
                highlight: -1,
                sortedStuff:[],
                swapped: [],
                info: "",
                // for merge
                sortedArray: [],
                sortingArray: [],
                sortingBorder: [],
                // for tempTable
                tempTable: [],
                // for Compare
                leftListN: [],
                rightListN: [],
                leftResN: [],
                rightResN: [],
                nowLeftIndex:-1,
                nowRightIndex: -1

        }
    }

    componentDidMount() {
        document.getElementById("input").value = "1,2,3,4,5,6,7,8";
    }

    handlePaused = (flag) => {
        // this.setState({isPaused: flag});
        before = pause;
        pause = flag;
    }


    handlePausedForLoop = () => {
        before = false;
        pause = true;
    }

    handleSpeed = (speed) => {
        let time;
        if (speed >= 80) {
            time = (900 - 9 * speed + 1);
        }
        else {
            time =  (1000 - 8 * speed);
        }
        this.setState({time})
    }

    changeSelect = () => {
        let select = !this.state.select;
        this.setState({select});
    }

    realOnReset = async () => {
        pause = true;

        this.changeSelect();
        await this.onReset();
        this.setState({changed: !this.state.changed});

        await sleep(1);
    }

    onReset = async () => {
        pause = true;
        let array = [...this.state.originalArray];
        reset = true;
        this.setState({reset: true,
            source:-1,
            array,
            pivot:-1,
            destination:-1,
            highlight: -1,
            sortedStuff:[],
             swapped: [],
            info:"",
            sortedArray: [],
                sortingArray: [],
                sortingBorder: [],
            tempTable: [],
            // for Compare
                leftListN: [],
                rightListN: [],
                leftResN: [],
                rightResN: [],
                nowLeftIndex:-1,
                nowRightIndex: -1
        });

        pause = before = false;
    }

    handleGenerate = async () => {
        let inputElement;
        await this.realOnReset();
        this.setState({reset: false});
        reset = false;

        inputElement = $("#input");
        const randomList = (n=undefined, range=undefined) => {
                let dict = {};
                let count = 0;
                let randomN;
                let randomRange = [...range];

                randomN = 0 | (1 + Math.random() * n);
                console.log("randomN", randomN)
                while (count < randomN) {
                    let random = 0 | (Math.random() * (randomRange[1] - randomRange[0] + 1) +  randomRange[0]);
                    if (!dict[random]) {
                        dict[random] = 1;
                        count ++;
                    }
                }
                let list = []
                for (let key in dict) {
                    list.push(key);
                }
                let length = list.length;
                const final = [];
                while (list.length !== 0) {
                    final.push(list.splice(Math.floor(Math.random() * list.length), 1));
                }
                return [randomN, final];
            }

        let [_, list] = randomList(20, [1,100]);
        inputElement.val(list.reduce((a, b) => {return a + "," + b;}));
        await sleep(100);
        await this.handleInput();
        if (this.state.algo !== 3) {
            this.setState({reset: true});
        reset = true;
        }


    }

    handleInput = async () => {
        try {
            let input = document.getElementById("input").value;
            input = input.split(",").map((x)=>parseInt(x));
            await this.setCustomState({originalArray: [...input], array: [...input]})
        } catch (e) {

        }
    }

    setAlgo = async (pos, val) => {
        if (pos === 0) {
            await this.realOnReset();
            this.setState({algo: val, nrFlag: val === 0});
        }
    }

    setCustomState = async (d, mode=0) => {
        if (reset) return ;
        this.setState(d);
        if (mode === 0)
            await this.stateAwait();
        else
            await sleep(this.state.time);
    }

    stateAwait = async () => {
        await sleep(1);
    }

    showMenu = () => {
        return <Menu
                handlePaused = {this.handlePaused}
                select = {this.state.select}
                onGenerate = {this.handleGenerate}
                algo={this.state.algo}
                handleSpeed = {this.handleSpeed}
                handleInput = {this.handleInput}
                setAlgo={this.setAlgo}
                onStart={this.addNumber}
                onReset={this.realOnReset}
                onChange={this.realOnReset}
        />;
    }

    addNumber = async ()=>{
        if (!before) {
            await this.onReset();
            await this.handleInput();

            await this.addNumberTemp();
            this.changeSelect();
            pause = before = false;

        }
    }

    addNumberTemp = async () => {
        this.setState({reset: false});
        reset = false;
        await this.handleInput();
        if (this.state.algo === 0) {
            await this.selection();
        } else if (this.state.algo === 1) {
            await this.insertion();
        } else if (this.state.algo === 2){
            await this.bubble();
        } else {
            await this.merge();
        }
    }

    setMessage = async (message) => {
        await this.setCustomState({info:message}, this.state.time);
    }


    setPivot = async (pivot, time=0) => {
        if (time === 0)
            await this.setCustomState({pivot}, this.state.time);
        else
            await this.setCustomState({pivot}, 1);
    }

    setArray = async (array) => {
        await this.setCustomState({array: [...array]});
    }

    setSourceDestination = async (source, destination)=> {
        await this.setCustomState({source, destination, }, this.state.time);
    }


    setHighlight = async (highlight, time=0) => {
        if (time === 0)
            await this.setCustomState({highlight}, this.state.time);
        else
            await this.setCustomState({highlight});
    }

    setPivotHighlight = async (pivot, highlight) => {
        await this.setCustomState({pivot, highlight}, this.state.time);
    }

    setSorted = async (i) => {
        let sortedStuff = [...this.state.sortedStuff];
        sortedStuff.push(i);
        await this.setCustomState({sortedStuff}, this.state.time);
    }

    setSortedAll = async (array) => {
        let sortedStuff = [...array];
        await this.setCustomState({sortedStuff}, this.state.time);
    }

    setInfo = async (info) => {
        await this.setCustomState({info}, this.state.time);
    }

    swap = (array, i, j) => {
        let temp = array[i];
        array[i] = array[j];
        array[j] = temp;
    }

    selection = async () => {
        let array = [...this.state.originalArray];
        for (let i = 0; i < array.length; i++) {
            let maxI = i;
            await this.setPivot(i);
            let loopInfo = "i = " + i ;
            for (let j = i + 1; j < array.length; j++) {
                let info = loopInfo + ", j = " + j;
                await this.setInfo(info);

                await this.setHighlight(j);
                if (array[j] > array[maxI]) {
                    maxI = j;
                }
            }
            if (i !== maxI) {
                this.swap(array, i, maxI);
                await this.setArray(array);
                await this.setSourceDestination(i, maxI);
                await this.setSourceDestination(-1, -1);
            }
            await this.setHighlight(-1, 1);

            await this.setSorted(i);
        }await this.setPivot(-1);
    }

    insertion = async () => {
        let array = [...this.state.originalArray];
        await this.setSorted(0);
        for (let i = 1; i < array.length; i ++) {
            let key = array[i];
            let j = i - 1;
            let loopInfo = "i = " + i ;
            while (j >= 0 && array[j] < key) {
                let info = loopInfo + ", j = " + j;
                await this.setInfo(info);

                await this.setPivotHighlight(j+1, j);
                this.swap(array, j, j + 1);
                await this.setArray(array);
                await this.setSourceDestination(j, j + 1);
                await this.setSourceDestination(-1, -1);
                await this.setSorted(j+1);
                j --;
            }
            await this.setHighlight(-1, 1);
            await this.setPivot(-1);
            await this.setSorted(i);
        }
        await this.setPivot(-1);
    }

    bubble = async () => {
        let array = [...this.state.originalArray];

        for (let i = 0; i < array.length - 1; i++) {
            let hasChange = false;
            let loopInfo = "i = " + i ;
            for (let j = 0; j < array.length - i - 1; j ++) {
                let info = loopInfo + ", j = " + j;
                await this.setInfo(info);
                await this.setPivotHighlight(j, j+1);
                if (array[j] < array[j + 1]) {
                    this.swap(array, j, j + 1);
                    await this.setArray(array);
                    await this.setSourceDestination(j, j + 1);
                    await this.setSourceDestination(-1, -1);
                    hasChange = true;
                }
            }
            await this.setHighlight(-1, 1);
            await this.setPivot(-1);
            await this.setSorted(array.length - 1 - i);
            if (!hasChange) {
                    break;
                }
        }
         await this.setSortedAll(array.map((_, index)=>index))
    }

    merge = async () => {
        let array = [...this.state.array];
        let tempArray = array.map(x=>0);
        const generateN = (l, r) => {
        let temp = [];
        for (let i = l; i <= r; i++) {
            temp.push(i);
        }
        return temp;
    }

        const changeToNull = (l, r, array) => {
            return array.map((x, index)=>index>=l && index<=r?"null": x);
        }

        const setToNull = async (l, r, array) => {
            await this.setCustomState({array: changeToNull(l, r, array)}, this.state.time);
        }

        const setTempTable = async (temp, type="null", index=-1) => {
            if (type !== "null") {
                await setNowIndex(index, type);
            }
            await this.setCustomState({tempTable: [...temp]}, this.state.time);

        }


        const setArray = async (array) => {
            await this.setCustomState({array: [...array]}, this.state.time);
        }

        const setSortingArray = async (l , r) => {
            await this.setCustomState({sortingArray: generateN(l, r)}, this.state.time);
        }

        const setSortingArrayBorder = async (l, r, border) => {
            await this.setCustomState({sortingArray: generateN(l, r), sortingBorder: [...border]}, this.state.time);
        }

        const setSortingBorder = async (border) => {
            await this.setCustomState({sortingBorder: [...border]}, this.state.time);
        }

        const initializeCompareArray = async (l, r, listRes, type) => {
            if (type === 'left') {
                await this.setCustomState({leftListN: generateN(l, r), leftResN: [...listRes]});
            } else {
                await this.setCustomState({rightListN: generateN(l, r), rightResN: [...listRes]});
            }
        }

        const setNowIndex = async (index, type)=> {
            if (type === 'left') await this.setCustomState({nowLeftIndex: index}, this.state.time);
            else await this.setCustomState({nowRightIndex: index}, this.state.time);
        }
        const mergeSort = async (seq, l, r) => {
            if (l >= r) return ;
            let mid = parseInt(((l + r) / 2).toString());
            let border = [...this.state.sortingBorder];
            border.push(mid);
            await setSortingBorder(border);

            await mergeSort(seq, l, mid);
            await mergeSort(seq, mid + 1, r);
            border = border.filter(x=>x!==mid);
            await setSortingArrayBorder(l, r, border);


            let temp = [];
            generateN(l, r).map(x=>temp.push("null"));
            await initializeCompareArray(l, mid, this.state.array.filter((x, index)=>index>=l&&index<=mid), 'left');
            await initializeCompareArray(mid + 1, r, this.state.array.filter((x, index) => index>=mid+1&&index<=r), 'right');
            await setToNull(l, r, this.state.array);
            await setTempTable(temp);

            let i = l, j = mid + 1, k = 0;
            while (i <= mid && j <= r) {
                if (array[i] >= array[j]){
                    temp[k] = array[i];
                    await setTempTable(temp, "left", i);
                    tempArray[k++] = array[i++];
                }
                else  {
                    temp[k] = array[j];
                    await setTempTable(temp, "right", j);
                    tempArray[k++] = array[j++];
                }
            }
            while(i <= mid) {
                temp[k] = array[i];
                await setTempTable(temp, "left", i);
                tempArray[k++] = array[i++];
            }
            while(j <= r)   {
                temp[k] = array[j];
                await setTempTable(temp, "right", j);
                tempArray[k++] = array[j++];
            }
            for(i = l, j = 0; i <= r; i++, j++) array[i] = tempArray[j];

            let sortedArray = [...this.state.sortedArray];
            generateN(l, r).map(x=>sortedArray.push(x));
            await setArray(array);
            await this.setCustomState({tempTable: []}, this.state.time);
            await this.setCustomState({sortingArray: [], leftListN: [], rightListN: [],
                sortedArray, nowLeftIndex: -1, nowRightIndex: -1}, this.state.time);


        }

        await mergeSort(array, 0, array.length - 1);
        console.log(array);
    }



    generateN = (list) => {
        return list.map((_, index)=>index);
    }

    displayAlgorithm = () => {

        if (this.state.algo !== 3)
            return (
                <React.Fragment>
                    <Card header={<strong>Original array</strong>}>
                        <ArrayContainer2
                            array={this.state.originalArray}
                            source={-1}
                            destination={-1}
                        />
                    </Card>
                    <Card
                        header={<strong>Sorting array(descending order)</strong>}
                        info = {this.state.info}
                    >
                    <ArrayContainer
                        array={this.state.array}
                        source={this.state.source}
                        destination={this.state.destination}
                        pivot={this.state.pivot}
                        highlightIndices = {this.state.highlight}
                        sortedIndices = {this.state.sortedStuff}
                        swapped={this.state.swapped}
                        swapTime={this.state.time}
                        reset={reset}
                    />
                </Card>
                    <Card header={<strong>Color mode</strong>}>
                        <ColorContainer/>
                    </Card>

                </React.Fragment>
                );
        return <React.Fragment>
                    <Card header={<strong>Array</strong>}>
                        <InitialArray
                            listN={this.generateN(this.state.array)}
                            listRes={this.state.array}
                            sortedArray={this.state.sortedArray}
                            sortingArray={this.state.sortingArray}
                            sortingBorder={this.state.sortingBorder}
                            reset={reset}
                        />
                    </Card>
                    <Card header={<strong>TempArray</strong>}>
                        <TempArray
                            listRes={this.state.tempTable}
                            sortedArray={this.state.sortedArray}
                            sortingArray={this.state.sortingArray}
                            reset={reset}
                        />
                    </Card>
                    <div className="row" >
                        <div className="col-6 border-start align-items-center" id="left-panel">
                            <Card header={<strong>Left</strong>}>
                                <TableForCompare
                                    listN={this.state.leftListN}
                                    listRes={this.state.leftResN}
                                    nowIndex={this.state.nowLeftIndex}
                                    reset={reset}
                        />
                            </Card>
                        </div>

                    <div className="col-6 border-end align-items-center" id="right-panel">
                           <Card header={<strong>Right</strong>}>
                                <TableForCompare
                                    listN={this.state.rightListN}
                                    listRes={this.state.rightResN}
                                    nowIndex = {this.state.nowRightIndex}
                                    reset={reset}/>

                            </Card>

                    </div>
                    </div>

                </React.Fragment>;
    }

    render = () =>  {
       return (
           <div >
               <Navbar
                    name={"Sort"}
                />
               {this.showMenu()}
               {this.displayAlgorithm()}
        </div>);
    }
}

const cancelAnimationFrame = window.cancelAnimationFrame
|| window.mozCancelAnimationFrame;

function animateTimeout(cb,time){
	let i = 0;
    let myRef = null;
    let rememberTime = time;
	myRef = requestAnimationFrame(function fn(){
        if (pause)
            time = 100000000000;
        else
            time = rememberTime;
		if(i > Math.floor(60 / (1000/time)) || reset){
			cb();
			cancelAnimationFrame(myRef);
            // return ;
		}
		i++;
		myRef = requestAnimationFrame(fn)
	})
}


function sleep(ms) {
    return new Promise(resolve => animateTimeout(resolve, ms));
}
